
package com.trydofor.id.logs;

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;

import com.trydofor.aloe.exception.Exceptions;

/**
 * the wrapped logger.
 * 
 * @author : Shi Rongjiu (www.trydofor.com)
 */
public final class Eventer {
    
    private final Logger                   logger;
    private transient Map<Event, LogLevel> eventLevel = new HashMap<Event, LogLevel>();
    private transient LogLevel             levelUnset = LogLevel.WARN;
    
    /**
     * constructor
     * 
     * @param logger the logger
     * @throws NullPointerException if logger is null
     */
    public Eventer(Logger logger) {
        Exceptions.checkNullPointer(logger);
        this.logger = logger;
    }
    
    /**
     * log a message and an exception for the event if log level are compatible.
     * 
     * @param event the event
     * @param cause the exception cause to log
     * @param message the message to log
     */
    public void log(Event event, Throwable cause, String message) {
        LogLevel level = getLogLevel(event);
        if (level == null) return;
        switch (level) {
            case TRACE:
                logger.trace(outline(event, message), cause);
                break;
            case DEBUG:
                logger.debug(outline(event, message), cause);
                break;
            case INFO:
                logger.info(outline(event, message), cause);
                break;
            case WARN:
                logger.warn(outline(event, message), cause);
                break;
            case ERROR:
                logger.error(outline(event, message), cause);
                break;
            default:
                return;
        }
    }
    
    /**
     * log a formated message and its parameters for the event if log level are compatible.
     * 
     * @param event the event
     * @param message the formated message
     * @param param the parameters injected into the message
     */
    public void log(Event event, String message, Object... param) {
        LogLevel level = getLogLevel(event);
        if (level == null) return;
        switch (level) {
            case TRACE:
                logger.trace(outline(event, message), param);
                break;
            case DEBUG:
                logger.debug(outline(event, message), param);
                break;
            case INFO:
                logger.info(outline(event, message), param);
                break;
            case WARN:
                logger.warn(outline(event, message), param);
                break;
            case ERROR:
                logger.error(outline(event, message), param);
                break;
            default:
                return;
        }
    }
    
    /**
     * register a log level for the event
     * 
     * @param event the event
     * @param level the log level
     * @throws NullPointerException if event or level is null.
     * @return the old one.
     * @see Map#put(Object, Object)
     */
    public LogLevel register(Event event, LogLevel level) {
        Exceptions.checkNullPointer(event);
        Exceptions.checkNullPointer(level);
        return eventLevel.put(event, level);
    }
    
    /**
     * register all mapped event-level
     * 
     * @param eventLevel the map
     * @throws NullPointerException if the event-level is null
     */
    public void registerAll(Map<Event, LogLevel> eventLevel) {
        Exceptions.checkNullPointer(eventLevel);
        this.eventLevel.putAll(eventLevel);
    }
    
    /**
     * merged current event-level to the new eventer's map,
     * then refer to new's event-level map
     * 
     * @param eventer the eventer
     * @throws NullPointerException if the eventer is null
     */
    public void mergeEventAndRefer(Eventer eventer) {
        Exceptions.checkNullPointer(eventer);
        eventer.eventLevel.putAll(this.eventLevel);
        this.eventLevel.clear();
        this.eventLevel = eventer.eventLevel;
    }
    
    /**
     * copy the registered event level map
     * 
     * @return the copy
     */
    public Map<Event, LogLevel> copyEventLevels() {
        Map<Event, LogLevel> copy = new HashMap<Event, LogLevel>();
        copy.putAll(eventLevel);
        return copy;
    }
    
    /**
     * get the log level for the event.
     * 
     * @param event the event
     * @return the log level.
     */
    public LogLevel getLogLevel(Event event) {
        if (event == null) return null;
        LogLevel level = eventLevel.get(event);
        return level == null ? levelUnset : level;
    }
    
    /**
     * the the wrapped logger
     * 
     * @return the logger
     */
    public Logger getLogger() {
        return logger;
    }
    
    /**
     * @return the levelUnset
     */
    public LogLevel getLevelUnset() {
        return levelUnset;
    }
    
    /**
     * @param levelUnset the levelUnset to set
     */
    public void setLevelUnset(LogLevel levelUnset) {
        this.levelUnset = levelUnset;
    }
    
    //-----------------------
    private String outline(Event event, String message) {
        StringBuilder sb = new StringBuilder();
        sb.append(toFullName(event));
        sb.append(" ");
        sb.append(message);
        
        return sb.toString();
    }
    
    /**
     * get full name of even.
     * eg: "com.trydofor.id.mina.LoggingFilter$Event.SESSION_CREATED"
     * 
     * @param e the event
     * @return the full name
     */
    public static String toFullName(Event e) {
        return e.getClass().getName() + "." + e.name();
    }
    
    /**
     * the event to log
     * 
     * @author : Shi Rongjiu (www.trydofor.com)
     */
    public static interface Event {
        
        /**
         * the event name
         * 
         * @return the name
         */
        String name();
    };
}
